package com.plusjun.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.plusjun.common.exception.NoStockException;
import com.plusjun.common.to.OrderTO;
import com.plusjun.common.to.mq.StockDetailTO;
import com.plusjun.common.to.mq.StockLockTO;
import com.plusjun.common.utils.PageUtils;
import com.plusjun.common.utils.Query;
import com.plusjun.common.utils.R;
import com.plusjun.gulimall.ware.dao.WareSkuDao;
import com.plusjun.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.plusjun.gulimall.ware.entity.WareOrderTaskEntity;
import com.plusjun.gulimall.ware.entity.WareSkuEntity;
import com.plusjun.gulimall.ware.feign.OrderFeignService;
import com.plusjun.gulimall.ware.feign.ProductFeinService;
import com.plusjun.gulimall.ware.service.WareOrderTaskDetailService;
import com.plusjun.gulimall.ware.service.WareOrderTaskService;
import com.plusjun.gulimall.ware.service.WareSkuService;
import com.plusjun.gulimall.ware.vo.OrderEntityVO;
import com.plusjun.gulimall.ware.vo.OrderItemVO;
import com.plusjun.gulimall.ware.vo.SkuHasStockVO;
import com.plusjun.gulimall.ware.vo.WareSkuLockVO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


@Service("wareSkuService")
@Slf4j
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {
    @Resource
    private WareSkuDao wareSkuDao;
    @Resource
    private ProductFeinService productFeinService;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private WareOrderTaskService wareOrderTaskService;
    @Resource
    private WareOrderTaskDetailService wareOrderTaskDetailService;
    @Resource
    private OrderFeignService orderFeignService;


    /**
     * 解锁库存方法
     *
     * @param skuId
     * @param wareId
     * @param num
     * @param taskDetailId
     */
    private void unLockStock(Long skuId, Long wareId, Integer num, Long taskDetailId) {
        wareSkuDao.unLockStock(skuId, wareId, num);
        // 更新工作单的状态
        WareOrderTaskDetailEntity entity = new WareOrderTaskDetailEntity();
        entity.setId(taskDetailId);
        entity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(entity);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();
        /**
         * skuId: 1
         * wareId: 1
         */
        String skuId = (String) params.get("skuId");
        if (StringUtils.isNotEmpty(skuId)) {
            wrapper.eq("sku_id", skuId);
        }
        String wareId = (String) params.get("wareId");
        if (StringUtils.isNotEmpty(wareId)) {
            wrapper.eq("ware_id", wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        // 判断还没有这个库存记录 --新增
        // 有了 --- 更新
        List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (!CollectionUtils.isEmpty(wareSkuEntities)) {
            // 新增
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setWareId(wareId);
            wareSkuDao.insert(wareSkuEntity);
            // 远程查询sku的名字 失败的话 try掉 不会回滚
            try {
                R info = productFeinService.info(skuId);
                Map<String, Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                if (info.getCode() == 0) {
                    wareSkuEntity.setSkuName((String) skuInfo.get("skuName"));
                }
            } catch (Exception e) {
            }
        } else {
            // 更新
            wareSkuDao.updateStock(skuId, wareId, skuNum);
        }
    }

    @Override
    public List<SkuHasStockVO> getSkusHasStock(List<Long> skuIds) {
        List<SkuHasStockVO> collect = skuIds.stream().map(skuId -> {
            SkuHasStockVO stockVO = new SkuHasStockVO();
            long count = baseMapper.getSkuStock(skuId);
            stockVO.setSkuId(skuId);
            if (count > 0) {
                stockVO.setHasStock(true);
            }
            return stockVO;
        }).collect(Collectors.toList());
        return collect;
    }

    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockVO vo) {
        /**
         * 保存库存工作单详情
         * 追溯。
         */
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(taskEntity);

        // 找到每个商品在哪个仓库都有库存
        List<OrderItemVO> locks = vo.getLocks();
        List<Long> skuIds = locks.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
        List<WareSkuEntity> skuStockStatusList = wareSkuDao.listWareIdHasSkuStock(skuIds);
        List<SkuWareHasStock> collect = locks.stream().map(item -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            List<Long> wareIds = skuStockStatusList.stream().filter(o -> Objects.equals(skuId, o.getSkuId())).map(o1 -> o1.getWareId()).collect(Collectors.toList());
            stock.setSkuId(skuId);
            stock.setWareId(wareIds);
            stock.setNum(item.getCount());
            return stock;
        }).collect(Collectors.toList());

        // 库存锁定
        for (SkuWareHasStock hasStock : collect) {
            Boolean skuStocked = false;
            Long skuId = hasStock.getSkuId();
            List<Long> wareIds = hasStock.getWareId();
            if (CollectionUtils.isEmpty(wareIds)) {
                throw new NoStockException(skuId);
            }
            for (Long wareId : wareIds) {
                Long count = wareSkuDao.lockSkuStock(skuId, wareId, hasStock.getNum());
                if (count == 1) {
                    skuStocked = true;
                    // 当前库存锁定成功,给rabbitMQ发消息 锁定成功，进行一系列操作
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(null, skuId, "", hasStock.getNum(), taskEntity.getId(), wareId, 1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);
                    // 发送给MQ
                    StockLockTO stockLockTO = new StockLockTO();
                    stockLockTO.setId(taskEntity.getId());
                    stockLockTO.setDetailId(wareOrderTaskDetailEntity.getId());
                    StockDetailTO stockDetailTO = new StockDetailTO();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity, stockDetailTO);
                    stockLockTO.setStockDetailTO(stockDetailTO);
                    rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", stockLockTO);
                    break;
                } else {
                    // 当前库存锁定失败，重试下一个仓库
                }
            }
            if (!skuStocked) {
                // 当前商品所有仓库都没有锁住
                throw new NoStockException(skuId);
            }
        }
        // 3、全部锁定成功
        return true;
    }

    @Override
    public void unlockStock(StockLockTO to) {
        StockDetailTO detail = to.getStockDetailTO();
        Long detailId = detail.getId();
        /**
         * 解锁
         * 1、查询数据库关于这个订单的锁定库存信息
         * 有信息：
         *     解锁：订单情况
         *      1、没有这个订单，必须解锁
         *      2、有这个订单
         *          订单状态：取消了--> 解锁
         *                   没取消 -> 不解锁
         * 没有信息：
         *  库存锁定失败，库存回滚。无需解锁
         */
        WareOrderTaskDetailEntity byId = wareOrderTaskDetailService.getById(detailId);
        if (byId != null) {
            // 解锁
            Long id = to.getId();
            // 根据所库存的id去查到订单号
            WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getById(id);
            String orderSn = wareOrderTaskEntity.getOrderSn();
            // 根据订单号去查询订单状态
            R r = orderFeignService.getOrderStatus(orderSn);
            if (r.getCode() == 0) {
                OrderEntityVO data = (OrderEntityVO) r.getData(new TypeReference<OrderEntityVO>() {
                });
                if (data == null || data.getStatus() == 4) {
                    // 取消订单了或订单不存在,需要解锁库存
                    if (byId.getLockStatus().equals(1)) {
                        // 当前工作单详情，状态1 已锁定，但是未解锁才可以解锁
                        unLockStock(detail.getSkuId(), detail.getWareId(), detail.getSkuNum(), detailId);
                    }
                }
            } else {
                throw new RuntimeException("远程服务失败");
            }
        } else {
            // 无需解锁
        }
    }

    @Transactional
    @Override
    public void unlockStock(OrderTO to) {
        String orderSn = to.getOrderSn();
        // 查一下最新的库存解锁状态
        WareOrderTaskEntity entity = wareOrderTaskService.getOrderTasjByOrderSn(orderSn);
        Long id = entity.getId();
        // 按照工作单id，找到未解锁的进行解锁
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", id).eq("lock_statul", 1));
        for (WareOrderTaskDetailEntity item : list) {
            unLockStock(item.getSkuId(),entity.getWareId(),item.getSkuNum(),item.getId());
        }
    }

    @Data
    class SkuWareHasStock {
        private Long skuId;
        private Integer num;
        private List<Long> wareId;
    }

}